using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace AnguaRPGEngine.Library.Sockets
{
    #region HostSocket Region
    /// <summary>
    /// Handles listening and accepting connections from client sockets. Derived from !BaseSocket.
    /// </summary>
    public class HostSocket : BaseSocket
    {
        /// <summary>
        /// raised when a client connection request is accepted
        /// </summary>
        public event AcceptEventHandler OnAccept;
        private int port;
        private TcpListener tcpLsn;
        //private Thread listenerThread;

        #region Constructor Region
        /// <summary>
        /// constuctor
        /// </summary>
        public HostSocket()
            : base()
        {
        }
        #endregion

        #region Listen/Accept Region
        /// <summary>
        /// places the socket in a listening state
        /// </summary>
        /// <param name="port">port on which to listen</param>
        /// <returns>-1 if failed, greater than -1 if success</returns>
        public int StartListening(int port)
        {
            return StartListening(port, -1);
        }

        /// <summary>
        /// places he socket in a listening state
        /// </summary>
        /// <param name="port">port on which to listen</param>
        /// <param name="backLog">size of backlog queue</param>
        /// <returns>-1 if failed, greater than -1 if success</returns>
        public int StartListening(int port, int backLog)
        {
            int rc = 0;
            this.port = port;

            try
            {
                IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, port);
                tcpLsn = new TcpListener(localEndPoint);
                if (backLog == -1)
                {
                    tcpLsn.Start();
                }
                else
                {
                    tcpLsn.Start(backLog);
                }

                tcpLsn.BeginAcceptSocket(new AsyncCallback(SocketConnectingCallback), null);

                //BV - 2008/10/16 - replacing with .NET asynchronous accept
                //listenerThread = new Thread(new ThreadStart(WaitingForClient));
                //listenerThread.Name = "ListenerThread";
                //listenerThread.Start();
            }
            catch (SocketException se)
            {
                rc = -1;
                RaiseOnSocketError(se);
            }
            catch (Exception e)
            {
                rc = -2;
                RaiseOnGeneralError(e);
            }
            return rc;
        }

        private void SocketConnectingCallback(IAsyncResult asyncResult)
        {

            try
            {
                //get the socket trying to connect
                Socket connectingSocket = (Socket)tcpLsn.EndAcceptSocket(asyncResult);

                //create a client instance from it
                ClientSocket clientSocketToPass = new ClientSocket(connectingSocket);

                //pass the client up to subscribers
                RaiseOnAccept(clientSocketToPass);

                //accept new connections
                tcpLsn.BeginAcceptSocket(SocketConnectingCallback, null);
            }
            catch (SocketException socketEx)
            {
                RaiseOnSocketError(socketEx);
            }
            catch (Exception Ex)
            {
                RaiseOnGeneralError(Ex);
            }
        }


        //BV - 2008/10/16 - using .NET asynchonous acceptance functionality
        ///// <summary>
        ///// waits for a client to connect
        ///// </summary>
        //private void WaitingForClient()
        //{
        //    try
        //    {
        //        while (true)
        //        {
        //            try
        //            {
        //                // Accept will block until someone connects
        //                Socket sckt = tcpLsn.AcceptSocket();
        //                ClientSocket clientSocket = new ClientSocket(sckt);
        //                lock (this)
        //                {
        //                    RaiseOnAccept(clientSocket);
        //                    ResetClientThread();
        //                }
        //            }
        //            catch (SocketException se)
        //            {
        //                RaiseOnSocketError(se);
        //                ResetClientThread();
        //                return;
        //            }
        //            catch (System.Exception e)
        //            {
        //                RaiseOnGeneralError(e);
        //                ResetClientThread();
        //                return;
        //            }
        //        }
        //    }
        //    catch (System.Threading.ThreadAbortException caught)
        //    {
        //        ResetClientThread();
        //    }
        //}

        ///// <summary>
        ///// resets the thread listening for client connections
        ///// </summary>
        //private void ResetClientThread()
        //{
        //    if (listenerThread == null) return;
        //    if (listenerThread.IsAlive)
        //        return;
        //    listenerThread = new Thread(new ThreadStart(WaitingForClient));
        //    listenerThread.Name = "clientThread";
        //    listenerThread.Start();
        //}


        #endregion

        #region Host Socket Event Region
        /// <summary>
        /// raises the socket's OnAccept event
        /// </summary>
        /// <param name="cs">newly accepted client socket</param>
        private void RaiseOnAccept(ClientSocket cs)
        {
            if (OnAccept != null)
            {
                OnAccept(cs);
            }
        }
        #endregion

        #region IDisposable Region
        /// <summary>
        /// disposes of the socket
        /// </summary>
        public override void Dispose()
        {
            base.Dispose();
            try
            {
                tcpLsn.Stop();
            }
            catch { }
            tcpLsn = null;
        }
        #endregion
    }
    #endregion
}
